package us.codecraft.webmagic.core.downloader.okhttp;

import com.alibaba.fastjson.JSON;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.core.*;
import us.codecraft.webmagic.core.downloader.AbstractDownloader;
import us.codecraft.webmagic.core.selector.PlainText;
import us.codecraft.webmagic.core.utils.HttpConstant;

import java.io.Closeable;
import java.io.IOException;

/**
 * @author zhangx <br /> <p>fishlikewater@126.com</p><br />
 * <p> 使用okhttp作为下载客户端</p>
 * @Date 2019年01月24日 12:32
 * @since 0.7.4
 **/
public class OkhttpDownloader extends AbstractDownloader implements Closeable {

    private Logger log = LoggerFactory.getLogger(getClass());

    private OkHttpClient okHttpClient;

    private boolean isOpenAsync;

    public OkhttpDownloader(OkhttpConfig config){
        if (okHttpClient == null) {
            okHttpClient = new OkhttpClientBuild().okHttpClient(config);
        }
        this.isOpenAsync = config.isOpenAsync();
    }

    /**
     * 判断是否执行完成
     * @return
     */
    @Override
    public boolean isComplete(){
        int queueCount = okHttpClient.dispatcher().queuedCallsCount();
        int runningCount = okHttpClient.dispatcher().runningCallsCount();
        if(queueCount == 0 && runningCount == 0){
            return true;
        }
        return false;
    }

    @Override
    public boolean isOpenAsync(){
        return isOpenAsync;
    }

    /**
     * 释放资源
     */
    @Override
    public void close() {
        log.info("close okhttp dowanload...");
        okHttpClient.dispatcher().executorService().shutdown();
        okHttpClient.connectionPool().evictAll();
        if (okHttpClient.cache() != null) {
            try {
                okHttpClient.cache().close();
            } catch (IOException e) {
                log.error("close cache error", e);
            }
        }
        log.info("close okhttp dowanload successful");
    }

    @Override
    public void asyncDowanload(Request request, Task task){
        if (task == null || task.getSite() == null) {
            throw new NullPointerException("task or site can not be null");
        }
        request.getHeaders().putAll(task.getSite().getHeaders());
        try {
            Callback callback = new OkhttpCallback(request, task);
            String method = request.getMethod()==null?"GET":request.getMethod();
            if(method.equals(HttpConstant.Method.POST)){
                String paramsType = request.getParamsType();
                if(StringUtils.equals(HttpConstant.ParamsType.JSON, paramsType)){
                    String json = JSON.toJSONString(request.getParams());
                    OkHttpUtil.post(request.getUrl(), json, callback, request.getHeaders(), okHttpClient);
                }else {
                    OkHttpUtil.post(request.getUrl(), request.getParams(), request.getHeaders(), callback, okHttpClient);
                }
            }else {
                OkHttpUtil.get(request.getUrl(),null, request.getHeaders(),callback, okHttpClient);
            }
        }catch (Exception e){
            log.error("okhttp asyncdownload page happend error", e);
            onError(request);
            ((XSpider)task).onDownloaderFail(request);
        }
    }

    @Override
    public Page download(Request request, Task task) {
        if (task == null || task.getSite() == null) {
            throw new NullPointerException("task or site can not be null");
        }
        request.getHeaders().putAll(task.getSite().getHeaders());
        Page page = Page.fail();
        Response respons = null;
        try {
            String method = request.getMethod()==null?"":request.getMethod();
            switch (method) {
                case HttpConstant.Method.POST:
                    String paramsType = request.getParamsType();
                    if(StringUtils.equals(HttpConstant.ParamsType.JSON, paramsType)){
                        String json = JSON.toJSONString(request.getParams());
                        respons = OkHttpUtil.postJson(request.getUrl(), json, request.getHeaders(), okHttpClient);
                    }else {
                        respons = OkHttpUtil.postForm(request.getUrl(), request.getParams(), request.getHeaders(), okHttpClient);
                    }
                    break;
                default:
                    respons = OkHttpUtil.get(request.getUrl(), request.getHeaders(), okHttpClient);
                    break;
            }
            if(respons.isSuccessful()){
                page = handleResponse(respons, request, task.getSite());
                onSuccess(request);
                log.info("okhttp syncdownloading page success {}", request.getUrl());
                return page;
            }
            return page;
        }catch (Exception e){
            log.error("okhttp syncdownloading page happend error", e);
            onError(request);
            return page;
        }finally {
            if(respons != null){
                respons.close();
            }
        }
    }

    private Page handleResponse(Response respons, Request request, Site site) throws IOException {
        byte[] bytes = respons.body().bytes();
        Page page = new Page();
        page.setUrl(new PlainText(request.getUrl()));
        page.setRequest(request);
        page.setStatusCode(respons.code());
        page.setDownloadSuccess(true);
        page.setBytes(bytes);
        page.setRawText(new String(bytes, site.getCharset()));
        return page;
    }

    @Override
    public void setThread(int threadNum) {

    }

}
